{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "useful-nerve",
   "metadata": {},
   "source": [
    "**代码演示部分：配合本章学习材料使用**  \n",
    "**第一部分：张量运算示例**  \n",
    "这里将演示Tensor的一些基本操作"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "medical-utility",
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "opening-boards",
   "metadata": {},
   "outputs": [],
   "source": [
    "?torch.tensor"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "healthy-seventh",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor(1.) tensor(1) tensor(1, dtype=torch.int8)\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/tmp/ipykernel_11770/1264937814.py:4: DeprecationWarning: an integer is required (got type float).  Implicit conversion to integers using __int__ is deprecated, and may be removed in a future version of Python.\n",
      "  c = torch.tensor(1.0, dtype=torch.int8)\n"
     ]
    }
   ],
   "source": [
    "# 创建tensor，用dtype指定类型。注意类型要匹配\n",
    "a = torch.tensor(1.0, dtype=torch.float)\n",
    "b = torch.tensor(1, dtype=torch.long)\n",
    "c = torch.tensor(1.0, dtype=torch.int8)\n",
    "print(a, b, c)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "funny-complexity",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([[ 7.2398e-07,  4.5710e-41, -2.0912e+23],\n",
      "        [ 3.0812e-41,  6.7262e-43,  0.0000e+00]]) \n",
      " tensor([64,  0], dtype=torch.int32) \n",
      " tensor([1, 2, 3, 4], dtype=torch.int32)\n"
     ]
    }
   ],
   "source": [
    "# 使用指定类型函数随机初始化指定大小的tensor\n",
    "d = torch.FloatTensor(2,3)\n",
    "e = torch.IntTensor(2)\n",
    "f = torch.IntTensor([1,2,3,4])  #对于python已经定义好的数据结构可以直接转换\n",
    "print(d, '\\n', e, '\\n', f)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "welsh-blank",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([[1, 2, 3],\n",
      "        [4, 5, 6]])\n",
      "tensor([[1, 2, 3],\n",
      "        [4, 5, 6]])\n",
      "[[1 2 3]\n",
      " [4 5 6]]\n"
     ]
    }
   ],
   "source": [
    "# tensor和numpy array之间的相互转换\n",
    "import numpy as np\n",
    "\n",
    "g = np.array([[1,2,3],[4,5,6]])\n",
    "h = torch.tensor(g)\n",
    "print(h)\n",
    "i = torch.from_numpy(g)\n",
    "print(i)\n",
    "j = h.numpy()\n",
    "print(j)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6bc6370e",
   "metadata": {},
   "source": [
    "注意：torch.tensor创建得到的张量和原数据是不共享内存的，张量对应的变量是独立变量。  \n",
    "而torch.from_numpy()和torch.as_tensor()从numpy array创建得到的张量和原数据是共享内存的，张量对应的变量不是独立变量，修改numpy array会导致对应tensor的改变。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "aad3889d",
   "metadata": {},
   "outputs": [],
   "source": [
    "g[0,0] = 100\n",
    "print(i)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "empty-blocking",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([[0.2652, 0.0650, 0.5593],\n",
      "        [0.7864, 0.0015, 0.4458]]) \n",
      " tensor([[1., 1., 1.],\n",
      "        [1., 1., 1.]]) \n",
      " tensor([[0., 0., 0.],\n",
      "        [0., 0., 0.]]) \n",
      " tensor([0, 2, 4, 6, 8])\n"
     ]
    }
   ],
   "source": [
    "# 常见的构造Tensor的函数\n",
    "k = torch.rand(2, 3) \n",
    "l = torch.ones(2, 3)\n",
    "m = torch.zeros(2, 3)\n",
    "n = torch.arange(0, 10, 2)\n",
    "print(k, '\\n', l, '\\n', m, '\\n', n)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "characteristic-lighting",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "torch.Size([2, 3])\n",
      "torch.Size([2, 3])\n"
     ]
    }
   ],
   "source": [
    "# 查看tensor的维度信息（两种方式）\n",
    "print(k.shape)\n",
    "print(k.size())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "dress-baptist",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([[1.2652, 1.0650, 1.5593],\n",
      "        [1.7864, 1.0015, 1.4458]])\n"
     ]
    }
   ],
   "source": [
    "# tensor的运算\n",
    "o = torch.add(k,l)\n",
    "print(o)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "vocational-polymer",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([1.0650, 1.0015])\n",
      "tensor([1.2652, 1.0650, 1.5593])\n"
     ]
    }
   ],
   "source": [
    "# tensor的索引方式与numpy类似\n",
    "print(o[:,1])\n",
    "print(o[0,:])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "removed-lawrence",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([[1.2652, 1.0650],\n",
      "        [1.5593, 1.7864],\n",
      "        [1.0015, 1.4458]])\n",
      "tensor([[1.2652, 1.0650],\n",
      "        [1.5593, 1.7864],\n",
      "        [1.0015, 1.4458]])\n"
     ]
    }
   ],
   "source": [
    "# 改变tensor形状的神器：view\n",
    "print(o.view((3,2)))\n",
    "print(o.view(-1,2))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "yellow-wireless",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([[1, 2]])\n",
      "tensor([[1],\n",
      "        [2],\n",
      "        [3]])\n",
      "tensor([[2, 3],\n",
      "        [3, 4],\n",
      "        [4, 5]])\n"
     ]
    }
   ],
   "source": [
    "# tensor的广播机制（使用时要注意这个特性）\n",
    "p = torch.arange(1, 3).view(1, 2)\n",
    "print(p)\n",
    "q = torch.arange(1, 4).view(3, 1)\n",
    "print(q)\n",
    "print(p + q)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "paperback-rental",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([[1.2652, 1.0650, 1.5593],\n",
      "        [1.7864, 1.0015, 1.4458]])\n",
      "tensor([[[1.2652, 1.0650, 1.5593]],\n",
      "\n",
      "        [[1.7864, 1.0015, 1.4458]]])\n",
      "torch.Size([2, 1, 3])\n"
     ]
    }
   ],
   "source": [
    "# 扩展&压缩tensor的维度：squeeze\n",
    "print(o)\n",
    "r = o.unsqueeze(1)\n",
    "print(r)\n",
    "print(r.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "rising-madagascar",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([[[1.2652, 1.0650, 1.5593]],\n",
      "\n",
      "        [[1.7864, 1.0015, 1.4458]]])\n",
      "torch.Size([2, 1, 3])\n"
     ]
    }
   ],
   "source": [
    "s = r.squeeze(0)\n",
    "print(s)\n",
    "print(s.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "suited-poverty",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([[1.2652, 1.0650, 1.5593],\n",
      "        [1.7864, 1.0015, 1.4458]])\n",
      "torch.Size([2, 3])\n"
     ]
    }
   ],
   "source": [
    "t = r.squeeze(1)\n",
    "print(t)\n",
    "print(t.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "preliminary-pricing",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "entire-farmer",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "id": "deadly-young",
   "metadata": {},
   "source": [
    "**第二部分：自动求导示例**  \n",
    "这里将通过一个简单的函数  $y=x_1+2*x_2$  来说明PyTorch自动求导的过程"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "prostate-local",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor(5., grad_fn=<AddBackward0>)\n"
     ]
    }
   ],
   "source": [
    "import torch\n",
    "\n",
    "x1 = torch.tensor(1.0, requires_grad=True)\n",
    "x2 = torch.tensor(2.0, requires_grad=True)\n",
    "y = x1 + 2*x2\n",
    "print(y)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "grand-appliance",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "True\n",
      "True\n",
      "True\n"
     ]
    }
   ],
   "source": [
    "# 首先查看每个变量是否需要求导\n",
    "print(x1.requires_grad)\n",
    "print(x2.requires_grad)\n",
    "print(y.requires_grad)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "virgin-parameter",
   "metadata": {},
   "outputs": [
    {
     "ename": "AttributeError",
     "evalue": "'NoneType' object has no attribute 'data'",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mAttributeError\u001b[0m                            Traceback (most recent call last)",
      "\u001b[0;32m/tmp/ipykernel_11770/1707027577.py\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[0;31m# 查看每个变量导数大小。此时因为还没有反向传播，因此导数都不存在\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx1\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mgrad\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m      3\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx2\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mgrad\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      4\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0my\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mgrad\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mAttributeError\u001b[0m: 'NoneType' object has no attribute 'data'"
     ]
    }
   ],
   "source": [
    "# 查看每个变量导数大小。此时因为还没有反向传播，因此导数都不存在\n",
    "print(x1.grad.data)\n",
    "print(x2.grad.data)\n",
    "print(y.grad.data)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "patient-carpet",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor(1., requires_grad=True)"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "governing-arctic",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor(1.)\n",
      "tensor(2.)\n"
     ]
    }
   ],
   "source": [
    "## 反向传播后看导数大小\n",
    "y = x1 + 2*x2\n",
    "y.backward()\n",
    "print(x1.grad.data)\n",
    "print(x2.grad.data)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "id": "needed-harrison",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor(5.)\n",
      "tensor(10.)\n"
     ]
    }
   ],
   "source": [
    "# 导数是会累积的，重复运行相同命令，grad会增加\n",
    "y = x1 + 2*x2\n",
    "y.backward()\n",
    "print(x1.grad.data)\n",
    "print(x2.grad.data)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "vocational-thesaurus",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 所以每次计算前需要清除当前导数值避免累积，这一功能可以通过pytorch的optimizer实现。后续会讲到"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "id": "prostate-diagnosis",
   "metadata": {},
   "outputs": [
    {
     "ename": "RuntimeError",
     "evalue": "element 0 of tensors does not require grad and does not have a grad_fn",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mRuntimeError\u001b[0m                              Traceback (most recent call last)",
      "\u001b[0;32m/tmp/ipykernel_11770/4087792071.py\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m      3\u001b[0m \u001b[0mx2\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtorch\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtensor\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m2.0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mrequires_grad\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mFalse\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      4\u001b[0m \u001b[0my\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mx1\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0mx2\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 5\u001b[0;31m \u001b[0my\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbackward\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;32m/data1/ljq/anaconda3/envs/smp/lib/python3.8/site-packages/torch/_tensor.py\u001b[0m in \u001b[0;36mbackward\u001b[0;34m(self, gradient, retain_graph, create_graph, inputs)\u001b[0m\n\u001b[1;32m    253\u001b[0m                 \u001b[0mcreate_graph\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mcreate_graph\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    254\u001b[0m                 inputs=inputs)\n\u001b[0;32m--> 255\u001b[0;31m         \u001b[0mtorch\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mautograd\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbackward\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mgradient\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mretain_graph\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcreate_graph\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0minputs\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0minputs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    256\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    257\u001b[0m     \u001b[0;32mdef\u001b[0m \u001b[0mregister_hook\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mhook\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m/data1/ljq/anaconda3/envs/smp/lib/python3.8/site-packages/torch/autograd/__init__.py\u001b[0m in \u001b[0;36mbackward\u001b[0;34m(tensors, grad_tensors, retain_graph, create_graph, grad_variables, inputs)\u001b[0m\n\u001b[1;32m    145\u001b[0m         \u001b[0mretain_graph\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mcreate_graph\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    146\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 147\u001b[0;31m     Variable._execution_engine.run_backward(\n\u001b[0m\u001b[1;32m    148\u001b[0m         \u001b[0mtensors\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mgrad_tensors_\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mretain_graph\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcreate_graph\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0minputs\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    149\u001b[0m         allow_unreachable=True, accumulate_grad=True)  # allow_unreachable flag\n",
      "\u001b[0;31mRuntimeError\u001b[0m: element 0 of tensors does not require grad and does not have a grad_fn"
     ]
    }
   ],
   "source": [
    "# 尝试，如果不允许求导，会出现什么情况？\n",
    "x1 = torch.tensor(1.0, requires_grad=False)\n",
    "x2 = torch.tensor(2.0, requires_grad=False)\n",
    "y = x1 + 2*x2\n",
    "y.backward()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "according-outdoors",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "promotional-gilbert",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python [conda env:smp]",
   "language": "python",
   "name": "lijiaqi"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.11"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
